﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using MatlabBridge.NativeMethods;

namespace MatlabBridge
{
    public sealed class MatlabEngine : IDisposable
    {
        public MatlabEngine()
        {
            this.ep = libeng.engOpen(null);
            MatlabException.ThrowException("Could not open a session to the Matlab engine.", this.ep);
        }

        ~MatlabEngine()
        {
            this.Dispose(false);
        }
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected void Dispose(bool disposing)
        {
            if (ep == IntPtr.Zero) return;
            if (disposing)
            {
            }

            ClearResultBuffer();

            MatlabException.ThrowOperationFailedException("engClose",
                libeng.engClose(ep));
            ep = IntPtr.Zero;
        }

        protected IntPtr ep;
        public IntPtr UnmanagedPointer
        {
            get { return ep; }
        }

        public bool CommandWindowVisible
        {
            get
            {
                unsafe
                {
                    bool value;
                    MatlabException.ThrowOperationFailedException("engGetVisible",
                        libeng.engGetVisible(ep, &value));
                    return value;
                }
            }
            set
            {
                MatlabException.ThrowOperationFailedException("engSetVisible",
                    libeng.engSetVisible(ep, value));
            }
        }
        public MxArray this[string variable]
        {
            get
            {
                IntPtr pm = libeng.engGetVariable(ep, variable);
                MatlabException.ThrowException(string.Format("The named variable '{0}' does not exist.", variable), pm);
                return MxArray.FromPointer(pm);
            }
            set
            {
                MatlabException.ThrowOperationFailedException("engPutVariable",
                    libeng.engPutVariable(ep, variable, value.UnmanagedPointer));
            }
        }

        public void Execute(string command)
        {
            MatlabException.ThrowOperationFailedException("engEvalString",
                libeng.engEvalString(ep, command));
        }

        #region Output Buffer

        private IntPtr outputBuffer;
        private int outputSize;

        public string LastResult
        {
            get
            {
                if (outputBuffer == IntPtr.Zero)
                    throw new InvalidOperationException("Unable to get last Matlab output buffer.");
                return Marshal.PtrToStringAnsi(outputBuffer);
            }
        }

        public void SetResultBufferSize(int buffersize)
        {
            if (outputBuffer != IntPtr.Zero)
                ClearResultBuffer();

            if (buffersize != 0)
            {
                outputBuffer = Marshal.AllocHGlobal(buffersize);
                outputSize = buffersize;
            }

            NativeMethods.libeng.engOutputBuffer(ep, outputBuffer, outputSize);
        }
        private void ClearResultBuffer()
        {
            if (outputBuffer == IntPtr.Zero)
                return;
            
            Marshal.FreeHGlobal(outputBuffer);
            outputBuffer = IntPtr.Zero;
            outputSize = 0;
        }

        #endregion
    }
}
